//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _AK_ATOMIC_LOCK_H_
#define _AK_ATOMIC_LOCK_H_

//#define AK_MT

#ifdef AK_MT
#include "atomic.t"
#else
#include "non_atomic.h"
#endif

namespace ak
{
#ifdef AK_MT
	namespace tm=::std;

	class atomic_lock_base
	{
		static const size_t SMP_ATTEMPTS = 5;
		typedef tm::atomic<void*> val_t;
	public:
		atomic_lock_base(val_t& src)
			: ptr(src)
		{
			val = ptr.exchange(locked_indicator(), tm::memory_order_acquire);
			if (val == locked_indicator())
				spin_lock();
		}
		atomic_lock_base(const atomic_lock_base& src)
			: val(src.val)
			, ptr(src.ptr)
		{
			src.val = locked_indicator();
		}
		~atomic_lock_base()
		{
			if (val != locked_indicator())
				ptr.store(val, tm::memory_order_release);
		}
	protected:
		mutable void* val;
		val_t& ptr;

		static void* locked_indicator()
		{
			return reinterpret_cast<void*>(-1);
		}
		void spin_lock()
		{
			for (size_t smp_count = SMP_ATTEMPTS;;)
			{
				if (--smp_count == 0)
				{
					smp_count = SMP_ATTEMPTS;
					tm::this_thread::yield();
				}
				val = ptr.exchange(locked_indicator(), tm::memory_order_acquire);
				if (val != locked_indicator())
					return;
				relax_cpu();
			}
		}
	};

	template<typename T> class atomic_lock: protected atomic_lock_base
	{
	public:
		atomic_lock(tm::atomic<T*>& src)
			: atomic_lock_base(src)
		{
		}
		~atomic_lock()
		{
		}

		atomic_lock<T>& operator= (T* v)
		{
			val = v;
			return *this;
		}

		operator T* ()
		{
			return reinterpret_cast<T*>(val);
		}

		T* operator-> ()
		{
			return reinterpret_cast<T*>(val);
		}
	};

#else

	namespace tm=non_atomic;

	template<typename T> class atomic_lock
	{
		tm::atomic<T*>& ptr;
	public:
		atomic_lock(tm::atomic<T*>& src)
			: ptr(src)
		{
		}

		atomic_lock<T>& operator= (T* v)
		{
			ptr = v;
			return *this;
		}

		operator T* ()
		{
			return ptr.val;
		}

		T* operator-> ()
		{
			return ptr.val;
		}
	};
#endif
}

#endif // _AK_ATOMIC_LOCK_H_
